// This code is hosted on http://code.google.com/p/lenthorp/
// Freely available for use in applications, but should NOT be modified
// Email all comments to lenthorpresearch@gmail.com

#ifndef quantlib_utilities_daterateholder_hpp
#define quantlib_utilities_daterateholder_hpp

#include <ql/types.hpp>
#include <ql/time/date.hpp>
#include <vector>
#include <map>
#include <algorithm>


namespace QuantLib {

	class DateRateHolder {
		public:
			DateRateHolder() {
			}

			void setElement(Date d, Real v) {
				_dates.push_back(d);
				_rates.push_back(v);
			}

			Date getElementDate(Size idx) {
				return _dates[idx];
			}

			Real getElementRate(Size idx) {
				return _rates[idx];
			}


			Size findDateIndex(Date d) {
				std::vector<Date>::iterator it;
				it = std::find(_dates.begin(), _dates.end(), d);
				return (it - _dates.begin());
			}

			Size size() {
				return _dates.size();
			}

			std::vector<Date> getAllDates() {
				return _dates;
			}

		private:
			std::vector<Date> _dates;
			std::vector<Real> _rates;
	};

	class DateMultiRateHolder {
		
		public:
			DateMultiRateHolder() {
			}

			// Adds new rate to the existing rates
			// Removes any elements where the dates do not match
			void addRates(std::string assetName, DateRateHolder drh) {

				std::vector<Date> newDates = std::vector<Date>();
				std::map<std::string, std::vector<Real> > newRates = std::map<std::string, std::vector<Real> >();

				if (_rates.size() == 0) {
					std::vector<Real> newEntry = std::vector<Real>();
					for (int idx = 0; idx < drh.size(); ++idx) {
						newDates.push_back(drh.getElementDate(idx));
						newEntry.push_back(drh.getElementRate(idx));
					}
					newRates[assetName] = newEntry;
				}

				else {
				
					std::map<std::string, std::vector<Real> >::iterator mt;
					//for (mt=_rates.begin(); mt!=_rates.end(); ++mt)
					//	newRates[mt->first] = std::vector<Real>(); // dummy for now - overwritten later on
					std::vector<Real> newEntry = std::vector<Real>();

					std::vector<Date>::iterator it;
					std::vector<int> indices = std::vector<int>();
					for (int idx = 0; idx < drh.size(); ++idx) {
						Date d = drh.getElementDate(idx);
						it = std::find(_dates.begin(), _dates.end(), d);
						if (it != _dates.end()) {
							newDates.push_back(d);
							newEntry.push_back(drh.getElementRate(idx));
							indices.push_back(it-_dates.begin());
						}
					}

					newRates[assetName] = newEntry;
					for (mt=_rates.begin(); mt!=_rates.end(); ++mt) {
						std::string name = mt->first;
						std::vector<Real> newRateVector(indices.size());
						for (int idx = 0; idx < indices.size(); ++idx) {
							newRateVector[idx] = mt->second[indices[idx]];
						}
						newRates[name] = newRateVector;
					}
				}

				_dates = newDates;
				_rates = newRates;
				
			}

			std::vector<Date> getAvailableDates() {
				return _dates;
			}

			Real getElementRate(Size idx, std::string rateName) {
				return (_rates.find(rateName)->second)[idx];
			}

			Date getElementDate(Size idx) {
				return _dates[idx];
			}

			Size findDateIndex(Date d) {
				std::vector<Date>::iterator it;
				it = std::find(_dates.begin(), _dates.end(), d);
				return (it - _dates.begin());
			}

		private:
			std::vector<Date> _dates;
			std::map<std::string, std::vector<Real> > _rates;

	};

}


#endif